#include "voice_server.h"
#include "message.h"
#include <fstream>


CVoiceEpollWatcher::CVoiceEpollWatcher()
{
	CFileDataHandle::GetInstance().ClearAllClientsFastdfsData();
	CFileDataHandle::GetInstance().ClearAllClients();
}

CVoiceEpollWatcher::~CVoiceEpollWatcher()
{
	CFileDataHandle::GetInstance().ClearAllClientsFastdfsData();
	CFileDataHandle::GetInstance().ClearAllClients();
}

bool CVoiceEpollWatcher::handle_clientData(EpollContext *epoll_context, char clientData[], int dataLength)
{
	if (epoll_context == nullptr)
	{
		FATAL_MSG("epoll_context is nullptr , dataLength: " << dataLength);	
		return false;
	}

	const uint16_t ui16MessageIDLength = sizeof(epoll_context->uiMessageID);
	memcpy(&epoll_context->uiMessageID, clientData, ui16MessageIDLength);

//	INFO_MSG("get client message ID: " << epoll_context->uiMessageID << "  size: " << dataLength);
	switch(epoll_context->uiMessageID)
	{
		case eVMP_ReqVerifyUser:				// verfiy clients
			{
				if (CFileDataHandle::GetInstance().HasThisClients(epoll_context))
				{
					ERROR_MSG("there is same cliet before client ip: " << epoll_context->client_ip.c_str() 
							<< ", client fd: " << epoll_context->fd  << " mesg ID: " << epoll_context->uiMessageID);
					return false;
				}

				stReqVerifyUser reqVerfiy ;
				memcpy(&reqVerfiy, clientData + ui16MessageIDLength, sizeof(stReqVerifyUser));
				// verify by uiUserID uiGameID uiZoneID & token
				
				



				// after check client , the next if legal client process
				CFileDataHandle::GetInstance().InsertNewClient(epoll_context->fd);
				epoll_context->stToClient = new stRtnVerifyUser();
				((stRtnVerifyUser*)epoll_context->stToClient)->uiRet = 0;

			}
			break;
		case eVMP_ReqBeginUpload:
			{
				if (!CFileDataHandle::GetInstance().HasThisClients(epoll_context))
				{
					ERROR_MSG("does not has this client or produce an error client ip: " << epoll_context->client_ip.c_str() 
							<< ", client fd: " << epoll_context->fd  << " mesg ID: " << epoll_context->uiMessageID);
					return false;
				}

				stReqBeginToUploadFile reqBeginUpload;
				memcpy(&reqBeginUpload, clientData + ui16MessageIDLength, sizeof(stReqBeginToUploadFile));

				// create data store stcture
				std::shared_ptr<SFastdfsData> clientFastDfsData;
				clientFastDfsData.reset(new SFastdfsData());
				memcpy(clientFastDfsData->extensionName,&reqBeginUpload.extensionName,sizeof(reqBeginUpload.extensionName));
				clientFastDfsData->totalLen = reqBeginUpload.uiFileSize;
				// allocate memory space
				clientFastDfsData->pVoicemessage_data = new char[reqBeginUpload.uiFileSize + 1];

				CFileDataHandle::GetInstance().InsertClientFastdfsData(epoll_context->fd,clientFastDfsData);
				epoll_context->stToClient = new stRtnBeginToUploadFile();
				((stRtnBeginToUploadFile*)epoll_context->stToClient)->uiFlag = 0;

			}
			break;
		case eVMP_ReqUploadVoiceMessage:
			{
				if (!CFileDataHandle::GetInstance().HasThisClients(epoll_context))
				{
					ERROR_MSG("does not has this client or produce an error client ip: " << epoll_context->client_ip.c_str() 
							<< ", client fd: " << epoll_context->fd  << " mesg ID: " << epoll_context->uiMessageID);
					return false;
				}

				std::shared_ptr<SFastdfsData> clientFastdfsData = 
					CFileDataHandle::GetInstance().GetClientData(epoll_context->fd);
				if (clientFastdfsData == nullptr) 
				{
					ERROR_MSG("illegal may client not send begin to send message  client ip: " 
							<< epoll_context->client_ip.c_str() 
							<< ", client fd: " << epoll_context->fd  << " mesg ID: " << epoll_context->uiMessageID);
					
					return false;	 
				}

				uint16_t ui16StReqStMsgLength = sizeof(stReqUploadVoiceMessageUser);
				uint16_t ui16HeadLength = ui16StReqStMsgLength + ui16MessageIDLength;
				uint16_t ui16FileDataLength = dataLength - ui16HeadLength;

				if (ui16FileDataLength > (clientFastdfsData->totalLen - clientFastdfsData->usedLen))
				{
					ERROR_MSG("recv file length bigger than client wanted max Length client ip: " 
							<< epoll_context->client_ip.c_str() 
							<< ", client fd: " << epoll_context->fd  << " msg ID: " << epoll_context->uiMessageID 
							<< "  FileDataLegth " << ui16FileDataLength << " max total length " << 
					clientFastdfsData->totalLen  << " used len: " << clientFastdfsData->usedLen);

					CFileDataHandle::GetInstance().DeleteOneClientFastdfsData(epoll_context->fd);
					return false;
				}

				
				stReqUploadVoiceMessageUser reqUploadData;
				memcpy(&reqUploadData, clientData + ui16MessageIDLength, ui16StReqStMsgLength);

				epoll_context->stToClient = new stRtnUploadVoiceMessageUser();
				if (!reqUploadData.isBegin() && !reqUploadData.isEnd())
				{
					// this is an error 
					ERROR_MSG("recv file can not be not begin and not end at the same time client ip: " 
							<< epoll_context->client_ip.c_str() 
							<< ", client fd: " << epoll_context->fd  << " mesg ID: " << epoll_context->uiMessageID
							<< "Msg length " << ui16StReqStMsgLength << "FileDataLegth " << ui16FileDataLength);

					CFileDataHandle::GetInstance().DeleteOneClientFastdfsData(epoll_context->fd);
					((stRtnUploadVoiceMessageUser*)epoll_context->stToClient)->uiRet = 1;
				}
				else if (reqUploadData.isBegin() && !reqUploadData.isEnd())
				{
					// means have others package later, need to store it
					memcpy(clientFastdfsData->pVoicemessage_data + clientFastdfsData->usedLen,
							clientData + ui16HeadLength, ui16FileDataLength);

					clientFastdfsData->usedLen += ui16FileDataLength; 
				}
				else
				{
					// last , need to send to fastdfs to store and delete the store memory
					if (clientFastdfsData->totalLen - clientFastdfsData->usedLen 
							!= ui16FileDataLength)
					{
						ERROR_MSG("last package recv file length is not equal to client wanted Length client ip: " 
								<< epoll_context->client_ip.c_str() << ", client fd: " << epoll_context->fd  
								<< " mesg ID: " << epoll_context->uiMessageID << " fastdfs totalLen " 
								<< clientFastdfsData->totalLen << " fastdfs usedLen " 
								<< clientFastdfsData->usedLen <<" FileDataLegth " << ui16FileDataLength);

						CFileDataHandle::GetInstance().DeleteOneClientFastdfsData(epoll_context->fd);
						return false;
					}

					memcpy(clientFastdfsData->pVoicemessage_data + clientFastdfsData->usedLen,
							clientData + ui16HeadLength, ui16FileDataLength);

					clientFastdfsData->usedLen += ui16FileDataLength; 
					// send to fastdfs to handle the client file data
					if (!COpFastDfs::GetInstance().StoreDataToFastDFS(*clientFastdfsData, 
								((stRtnUploadVoiceMessageUser*)epoll_context->stToClient)->cUrldata, 
								((stRtnUploadVoiceMessageUser*)epoll_context->stToClient)->cGroupname))
					{
						((stRtnUploadVoiceMessageUser*)epoll_context->stToClient)->uiRet = 1; 
					}

					CMd5 clientFileDataMD5(clientFastdfsData->pVoicemessage_data, clientFastdfsData->totalLen);

					memcpy(((stRtnUploadVoiceMessageUser*)epoll_context->stToClient)->cFileMd5,
						clientFileDataMD5.toString().c_str(), clientFileDataMD5.toString().length());

					CFileDataHandle::GetInstance().DeleteOneClientFastdfsData(epoll_context->fd);
				}
			}
			break;

		case eVMP_ReqDeleteUploadedFile:
			{
				if (!CFileDataHandle::GetInstance().HasThisClients(epoll_context))
				{
					ERROR_MSG("does not has this client or produce an error client ip: " << epoll_context->client_ip.c_str() 
							<< ", client fd: " << epoll_context->fd  << " mesg ID: " << epoll_context->uiMessageID);
					return false;
				}


			}
			break;

		default:
			{
				ERROR_MSG("message ID is wrong client ip: " << epoll_context->client_ip.c_str() 
						<< ", client fd: " << epoll_context->fd  << " mesg ID: " << epoll_context->uiMessageID);
				return false;
			}
			break;
	}

	return true;
}

int CVoiceEpollWatcher::on_accept(EpollContext &epoll_context)
{
	int conn_sock = epoll_context.fd;
	std::string clientIP = epoll_context.client_ip;

	DEBUG_MSG("accept client IP: " << clientIP.c_str() << ", connectd fd: " << conn_sock);
//	can add some time out delete clients operations

	return 0;
}

int CVoiceEpollWatcher::on_readable(int &epollfd, epoll_event &event)
{
	if (event.data.ptr == nullptr)
	{
		FATAL_MSG("event.data.ptr is nullptr , epollfd: " << epollfd);	
		return false;
	}

	EpollContext *epoll_context = (EpollContext *) event.data.ptr;
	int fd = epoll_context->fd;

	int buffer_size = SS_READ_BUFFER_SIZE;
	char read_buffer[buffer_size];        
	memset(read_buffer, 0, buffer_size);  

	int read_size = recv(fd, read_buffer, buffer_size, 0);
	if (read_size == -1 && errno == EINTR) {              
		    return READ_CONTINUE;                             
	}
	if (read_size == -1 /* io err*/|| read_size == 0 /* close */) 
	{
		    return READ_CLOSE;                                         
	}
	//DEBUG_MSG("read success which read size: " << read_size);


	// read_buffer can be parsed data here
	this->handle_clientData(epoll_context, read_buffer, read_size);


	return READ_OVER;
}

int CVoiceEpollWatcher::on_writeable(EpollContext &epoll_context)
{
	if (!CFileDataHandle::GetInstance().HasThisClients(&epoll_context))
	{
		ERROR_MSG("does not has this client or produce an error cient ip: " << epoll_context.client_ip.c_str() 
				<< ", client fd: " << epoll_context.fd  << " mesg ID: " << epoll_context.uiMessageID);
		return false;
	}

	int client_fd = epoll_context.fd;  
	int ret = 0;	
	
	// response data to client
	switch(epoll_context.uiMessageID)
	{
		case eVMP_ReqVerifyUser:
			{
				ret = send(client_fd, epoll_context.stToClient, sizeof(stRtnVerifyUser), 0); 
				delete (stRtnVerifyUser*)epoll_context.stToClient;
			}
			break;

		case eVMP_ReqBeginUpload:
			{
				ret = send(client_fd, epoll_context.stToClient, sizeof(stRtnBeginToUploadFile), 0); 
				delete (stRtnBeginToUploadFile*)epoll_context.stToClient;
			}

			break;

		case eVMP_ReqUploadVoiceMessage:
			{
				ret = send(client_fd, epoll_context.stToClient, sizeof(stRtnUploadVoiceMessageUser), 0); 
				delete (stRtnUploadVoiceMessageUser*)epoll_context.stToClient;
			}
			break;
		
		case eVMP_ReqDeleteUploadedFile:
			{
				ret = send(client_fd, epoll_context.stToClient, sizeof(stRtnDeleteUploadedFileUserCmd), 0); 
				delete (stRtnDeleteUploadedFileUserCmd*)epoll_context.stToClient;
			}

			break;
	}

	if (ret < 0)
	{
		ERROR_MSG("client ip: " << epoll_context.client_ip.c_str() << ", client fd: " << client_fd
				<< " mesg ID: " << epoll_context.uiMessageID);
	}

	return 0;
}

int CVoiceEpollWatcher::on_close(EpollContext &epoll_context)
{
	// need to delete all cache about this connect fd
	DEBUG_MSG("close client client ip: " << epoll_context.client_ip.c_str() << ", client fd: " << epoll_context.fd);
	
	// delete this client 
	CFileDataHandle::GetInstance().DeleteOneClient(epoll_context.fd);

	// if has data store , delete it 
	CFileDataHandle::GetInstance().DeleteOneClientFastdfsData(epoll_context.fd);
	
	return 0;
}

CVoiceServer::CVoiceServer(const std::string& cIP, const uint32_t& uiBacklog, 
		const uint32_t& uiMaxEvents ,const uint32_t& uiPort)
{
	this->cIP = cIP;
	this->uiBacklog = uiBacklog;
	this->uiMaxEvents = uiMaxEvents;
	this->uiPort = uiPort;
}

CVoiceServer::~CVoiceServer()
{
	epoll_socket.stop_epoll();
}


bool CVoiceServer::StartServer()
{
	epoll_socket.set_port(uiPort);
	epoll_socket.set_backlog(uiBacklog);
	epoll_socket.set_max_events(uiMaxEvents);
	epoll_socket.add_bind_ip(cIP);
	epoll_socket.set_watcher(new CVoiceEpollWatcher());

	if (epoll_socket.start_epoll() != 0) return false;
	return true;
}
